package Server.ExtComponents.socket;


import Server.ExtComponents.BaseConfig.MServerConfig;
import org.jboss.netty.bootstrap.Bootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public final class MServerFrameNettyStart implements NettyServer {
    private static final Logger LOG = LoggerFactory.getLogger(MServerFrameIdleStateCheckHandler.class);
    public static boolean debug;
    private ServerBootstrap bootstrap=null;
    private ExecutionHandler executionHandler=null;
    private final String[]optionList={"tcpNoDelay","keepAlive","readWriteFair","child.tcpNoDelay","child.reuseAddress","readWriteFair",};

    public MServerFrameNettyStart(){

    }
    @Override
    public void startServer(){
        try{
            //初始化netty配置
            boolean status=IntiServer();
            if(!status)System.exit(0);
            getServerBootstrap();
            configureServerBootStrap(this.bootstrap,optionList);
            if (MServerConfig.NETTY_DEBUG)LOG.info("server listen port is:"+ MServerConfig.GameserverPort);
        }catch (Exception e){
            LOG.error("netty start failed ", e);
            this.releaseServerResources();
        }

    }

    /**
     * 初始化server配置
     *
     * @return boolean
     */
    @Override
    public boolean IntiServer() {
        MServerConfig mServerConfig=new  MServerConfig();
        return mServerConfig.IntiServerConfig();  //To change body of implemented methods use File | Settings | File Templates.
    }


    @Override
    public void configureServerBootStrap(ServerBootstrap bootstrap,String[] optionsList) {
        // For clients who do not use spring.
        if(null == bootstrap){
            getServerBootstrap();
        }
        bootstrap.setPipelineFactory(getPipelineFactory());
        if (null != optionsList && optionsList.length > 0){
            for (String option : optionsList)
            {
                bootstrap.setOption(option, true);
            }
        }
        bootstrap.setOption("receiveBufferSize", 0x100000 * 0x40);
        bootstrap.setOption("child.receiveBufferSize", 0x100000 * 0x40);
        bootstrap.setOption("tcpNoDelay", 0);//Runtime ClassCastException
        bootstrap.bind(getInetSocketAddress());
    }


    @Override
    public ChannelPipelineFactory getPipelineFactory() {
        executionHandler = new ExecutionHandler(
                new MemoryAwareThreadPoolExecutor(MServerConfig.ExecutionHandler,
                        MServerConfig.maxChannelMemorySize, MServerConfig.maxTotalMemorySize,
                        MServerConfig.Idle, TimeUnit.SECONDS ,new MServerFrameObjectSizeEstimator()
                        ,new PriorityThreadFactory("executionHandlerThread",Thread.NORM_PRIORITY+1)));
        ChannelHandler idleStateHandler=new IdleStateHandler(MServerFrameUtil.timer, MServerConfig.readTimeOut, MServerConfig.writeTimeOut,0,TimeUnit.SECONDS);
        return new MServerFrameChannelPipelineFactory(idleStateHandler,executionHandler);
    }
    /**
     * @return Returns the created server bootstrap object.
     */
    @Override
    public Bootstrap getServerBootstrap() {
        ExecutorService pools=Executors.newCachedThreadPool();//boos线程（only one）和worker线程公用一个线程池！
        ChannelFactory factory=new NioServerSocketChannelFactory(pools, pools);
        bootstrap = new ServerBootstrap(factory);
        return bootstrap;  //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * 释放资源
     * in netty 4.0 http://netty.io/wiki/new-and-noteworthy.html
     * There's no more releaseExternalResources(). You can close all open channels immediately
     * and make all I/O threads stop themselves by calling EventLoopGroup.shutdown(),
     * just like you shut down your thread pool
     * with java.util.concurrent.ExecutorService.shutdown()
     */
    @Override
    public final void releaseServerResources(){

        bootstrap.releaseExternalResources();
        MServerFrameUtil.timer.stop();
//        allChannels.close();
        MServerFrameUtil.globalTrafficShapingHandler.releaseExternalResources();
    }

    /**
     * 网络地址
     *
     * @return
     */
    @Override
    public InetSocketAddress getInetSocketAddress() {
        return new InetSocketAddress(MServerConfig.GameserverPort);  //To change body of implemented methods use File | Settings | File Templates.
    }


    /**
     * 线程池工厂
     */
    private  class PriorityThreadFactory implements ThreadFactory{
        private int _prio;
        private String _name;
        private AtomicInteger _threadNumber = new AtomicInteger(1);
        private ThreadGroup _group;

        /**
         *
         * @param name 线程池名
         * @param priority   线程池优先级
         */
        public PriorityThreadFactory(String name, int priority){
            _prio = priority;
            _name = name;
            _group = new ThreadGroup(_name);
        }

        @Override
        public Thread newThread(Runnable r){
            Thread t = new Thread(_group, r);
            t.setName(_name + "-"+"#-" + _threadNumber.getAndIncrement());
            t.setPriority(_prio);
            return t;
        }

        public ThreadGroup getGroup(){
            return _group;
        }
    }


}
